﻿using jbt.CodeCandy.AsTrEAM.Client;
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;

namespace jbt.CodeCandy.AsTrEAM
{
    /// <summary>
    /// Creates <see cref="IStreamManagerServiceClient"/> objects.
    /// </summary>
    public class StreamServiceClientFactory
    {
        /// <summary>
        /// Creates a new <see cref="IStreamManagerServiceClient"/> object what connects to the specified address.
        /// </summary>
        /// <param name="remoteAddress">
        /// The address of service to connect to.
        /// </param>
        /// <returns>
        /// A new <see cref="IStreamManagerServiceClient"/> object what connects to the specified address.
        /// </returns>
        /// <exception cref="ArgumentException">
        /// <paramref name="remoteAddress"/> is null or white space.
        /// </exception>
        public IStreamManagerServiceClient ConnectTo(string remoteAddress)
        {
            if (String.IsNullOrWhiteSpace(remoteAddress))
            {
                throw new ArgumentException("Remote address is null or white space.", "remoteAddress");
            }

            var binding = CreateDefaultBinding();
            var address = new EndpointAddress(remoteAddress);

            var endpoint = CreateDefaultEndpoint(binding, address);

            var client = new StreamManagerServiceClient(endpoint);

            return client;
        }

        /// <summary>
        /// Creates a new <see cref="IStreamManagerServiceClient"/> object what connects to the specified address and is using the specified endpoint configuration.
        /// </summary>
        /// <param name="remoteAddress">
        /// The address of service to connect to.
        /// </param>
        /// <param name="endpointConfigurationName">
        /// Name of endpoint configuration to use.
        /// </param>
        /// <returns>
        /// A new <see cref="IStreamManagerServiceClient"/> object what connects to the specified address and is using the specified endpoint configuration.
        /// </returns>
        /// <exception cref="ArgumentException">
        /// <paramref name="remoteAddress"/> is null or white space.
        /// - or -
        /// <paramref name="endpointConfigurationName"/> is null or white space.
        /// </exception>
        public IStreamManagerServiceClient ConnectTo(string remoteAddress, string endpointConfigurationName)
        {
            if (String.IsNullOrWhiteSpace(remoteAddress))
            {
                throw new ArgumentException("Remote address is null or white space.", "remoteAddress");
            }

            if (String.IsNullOrWhiteSpace(endpointConfigurationName))
            {
                throw new ArgumentException("Endpoint configuration name is null or white space.", "endpointConfigurationName");
            }

            var client = new StreamManagerServiceClient(endpointConfigurationName, remoteAddress);

            return client;
        }

        /// <summary>
        /// Creates a new <see cref="IStreamManagerServiceClient"/> object what connects to the service in the specified endpoint configuration.
        /// </summary>
        /// <param name="endpointConfigurationName">
        /// Name of endpoint configuration to use.
        /// </param>
        /// <returns>
        /// A new <see cref="IStreamManagerServiceClient"/> object what connects to the service in the specified endpoint configuration.
        /// </returns>
        /// <exception cref="ArgumentException">
        /// <paramref name="endpointConfigurationName"/> is null or white space.
        /// </exception>
        public IStreamManagerServiceClient ConnectUsing(string endpointConfigurationName)
        {
            if (String.IsNullOrWhiteSpace(endpointConfigurationName))
            {
                throw new ArgumentException("Endpoint configuration name is null or white space.", "endpointConfigurationName");
            }

            var client = new StreamManagerServiceClient(endpointConfigurationName);

            return client;
        }

        /// <summary>
        /// Creates a new <see cref="IStreamManagerServiceClient"/> object what connects to the service in the specified endpoint.
        /// </summary>
        /// <param name="endpoint">
        /// The endpoint to use.
        /// </param>
        /// <returns>
        /// A new <see cref="IStreamManagerServiceClient"/> object what connects to the service in the specified endpoint.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="endpoint"/> is null.
        /// </exception>
        public IStreamManagerServiceClient ConnectUsing(ServiceEndpoint endpoint)
        {
            if (endpoint == null)
            {
                throw new ArgumentNullException("endpoint");
            }

            var client = new StreamManagerServiceClient(endpoint);

            return client;
        }

        private static BasicHttpBinding CreateDefaultBinding()
        {
            var binding = new BasicHttpBinding();
            binding.MaxReceivedMessageSize = Int32.MaxValue;
            binding.Name = "BasicHttpBindingStreamed";
            binding.TransferMode = TransferMode.Streamed;
            return binding;
        }

        private static ServiceEndpoint CreateDefaultEndpoint(Binding binding, EndpointAddress address)
        {
            var channelFactory = new ChannelFactory<IStreamManagerServiceClient>(binding, address);

            var endpoint = channelFactory.Endpoint;

            return endpoint;
        }
    }
}